package com.crm.miaohe.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;

import java.util.Objects;

@Slf4j
public class CrmAuthenticationManager implements AuthenticationManager {

    public static final String JWT_TOKEN_PREFIX = "auth:token_to_access:{0}";

    private static final OAuth2Error DEFAULT_INVALID_TOKEN =
            new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,
                    "An error occurred while attempting to decode the Jwt: Invalid token",
                    "");

    private static final OAuth2Error INVALID_TOKEN_TYPE =
            new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,
                    "An error occurred while attempting to decode the Jwt: Invalid token type",
                    "");

    private final ResourceServerTokenServices tokenServices;

    public CrmAuthenticationManager(ResourceServerTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (Objects.isNull(authentication)) {
            log.warn("无认证信息，认证失败。");
            throw new OAuth2AuthenticationException(DEFAULT_INVALID_TOKEN, DEFAULT_INVALID_TOKEN.getDescription());
        }

        if (authentication instanceof BearerTokenAuthenticationToken) {
            BearerTokenAuthenticationToken bearerToken = (BearerTokenAuthenticationToken) authentication;
            OAuth2Authentication auth = tokenServices.loadAuthentication(bearerToken.getToken());
            if (Objects.isNull(auth)) {
                throw new InvalidTokenException("Invalid token: " + bearerToken.getToken());
            }
            log.info("{} checked. [{}:{}]", "Authorization", auth.getName(), auth.getAuthorities());
            auth.setAuthenticated(true);
            return auth;
        } else if (authentication instanceof UsernamePasswordAuthenticationToken) {
            UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
            token.eraseCredentials();
            return token;
        } else {
            if (authentication.isAuthenticated()) {
                return authentication;
            }
            throw new OAuth2AuthenticationException(DEFAULT_INVALID_TOKEN, DEFAULT_INVALID_TOKEN.getDescription());
        }
    }
}
